package iit.projects.etaxi.view.bean;

import iit.projects.etaxi.model.dto.CitySCDO;
import iit.projects.etaxi.model.entity.City;
import iit.projects.etaxi.model.entity.SubCity;
import iit.projects.etaxi.model.service.CityService;
import iit.projects.etaxi.model.service.ServiceLocator;
import iit.projects.etaxi.model.service.SubCityService;
import iit.projects.etaxi.util.Constants;
import iit.projects.etaxi.util.JSFUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import javax.faces.component.UIInput;
import javax.faces.event.ActionEvent;
import javax.faces.event.ValueChangeEvent;
import javax.faces.model.SelectItem;

import ca.odell.glazedlists.FilterList;
import ca.odell.glazedlists.GlazedLists;
import ca.odell.glazedlists.TextFilterator;
import ca.odell.glazedlists.matchers.TextMatcherEditor;

import com.icesoft.faces.component.ext.HtmlSelectOneMenu;
import com.icesoft.faces.component.selectinputtext.SelectInputText;

public class SubCityProximityBackingBean {
	
	private String cityCode;
	
	private String subCityCode;
	
	private boolean citySelected;
	
	private String selectedCity;
	
	private String selectedSubCity;
	
	private List<City> cities;
	private List<SubCity> subCities;
	private List<SubCity> proximitySubCities;
	
	/**
	 * <p>Data shown in the table</p>
	 */
	private List<SubCity> subsInProximity;
	private List<SelectItem> cityList;
	private List<SelectItem> subCityList;
	private List<SelectItem> proximityCityList;
	private List<SelectItem> proximitySubCityList;
	
	private HtmlSelectOneMenu compCitylist;
	private HtmlSelectOneMenu compSubCitylist;
	private SelectInputText compSelectCity;
	private SelectInputText compSelectSubCity;
	private TextMatcherEditor<SelectItem> cityNameMatcherEditor;
	private TextMatcherEditor<SelectItem> subCityNameMatcherEditor;
	
	private ServiceLocator serviceLocator;
	private SubCityService subCityService;
	private CityService cityService;
	
	private static final String STATIC_SELECT_CITY_MSG = 
			JSFUtils.getResourcesProperty("manage.subcityproximity.city.label");
	
	private static final String STATIC_SELECT_SUBCITY_MSG = 
		JSFUtils.getResourcesProperty("manage.subcityproximity.subcities.label");
	
	private static final String STATIC_TABLE_HEADER = 
		JSFUtils.getResourcesProperty("manage.subcityproximity.table.header");
	
	private String selectCityMsg;
	private String selectSubCityMsg;
	private String tableHeader;
	
	
	@javax.annotation.PostConstruct
	public void init(){
		cityService = serviceLocator.getCityService();
		subCityService = serviceLocator.getSubCityService();
		loadCityList();
		
		//Copy the values of cityList into proximityCityList and remove the '-select-' value
		proximityCityList = new ArrayList<SelectItem>(this.cityList);
		proximityCityList.remove(0);
		
		subCityList = new ArrayList<SelectItem>();
		subCityList.add(new SelectItem(Constants.SELECT_VALUE, Constants.SELECT_LABEL));
		
		selectCityMsg = STATIC_SELECT_CITY_MSG;
		selectSubCityMsg = STATIC_SELECT_SUBCITY_MSG;
		tableHeader = STATIC_TABLE_HEADER;
		
		TextFilterator<SelectItem> cityNameSelectItemFilterator = new TextFilterator<SelectItem>() {
			@Override
			public void getFilterStrings(List<String> baseList,
					SelectItem element) {
				baseList.add(element.getLabel());
			}
		};
		TextFilterator<SelectItem> subCityNameNameSelectItemFilterator = new TextFilterator<SelectItem>() {
			@Override
			public void getFilterStrings(List<String> baseList,
					SelectItem element) {
				baseList.add(element.getLabel());
			}
		};
		
		cityNameMatcherEditor = new TextMatcherEditor<SelectItem>(cityNameSelectItemFilterator);
		subCityNameMatcherEditor = new TextMatcherEditor<SelectItem>(subCityNameNameSelectItemFilterator);
		
		this.proximityCityList = new FilterList<SelectItem>(GlazedLists.eventList(proximityCityList), cityNameMatcherEditor);
	}
	
	private void loadCityList(){
		this.cities = null;
		try{
			//Find all Cities.
			this.cities = cityService.findCities(new CitySCDO());
		}catch(Throwable e){
			return;
		}
		
		if(this.cities != null && !this.cities.isEmpty()){
			cityList = new ArrayList<SelectItem>();
			cityList.add(new SelectItem(Constants.SELECT_VALUE, Constants.SELECT_LABEL));
			
			for(City city : this.cities){
				cityList.add(new SelectItem(city.getCode(), city.getName()));
			}
		}
	}
	
	public void valueChangeCity(ValueChangeEvent event){
		
		String cityCode = (String)event.getNewValue();
		if(cityCode != null){
			try {
				City city  = findCityByCode(cityCode);
				this.subCities = new ArrayList<SubCity>(city.getSubcities());
				
				clear();
				
				if(subCities != null && !subCities.isEmpty()){
					for(SubCity subCity : subCities){
						this.subCityList.add(new SelectItem(subCity.getCode(), subCity.getName()));
					}
				}
				else{
					clear();
				}
			} catch (Throwable e) {
				return;
			}
		}
		else{
			clear();
		}
	}
	
	public void citySelect(ValueChangeEvent event){
		if (event.getComponent() instanceof SelectInputText) {
			
			SelectInputText component = (SelectInputText)event.getComponent();
			
			String value = (String) event.getNewValue();
			cityNameMatcherEditor.setMode(Constants.TEXT_MATCHER_EDITOR_MODE);
			cityNameMatcherEditor.setFilterText((value == null) ? new String[] {} : new String[] { value });
			
			SelectItem selectedItem = component.getSelectedItem();
			
			if(selectedItem != null){
				String cityCode = (String) selectedItem.getValue();
				
				City city  = findCityByCode(cityCode);
				
				this.proximitySubCities = new ArrayList<SubCity>(city.getSubcities());
				
				if(this.proximitySubCities != null && !this.proximitySubCities.isEmpty()){
					this.proximitySubCityList = new ArrayList<SelectItem>();
					for(SubCity subCity : this.proximitySubCities){
						if(!city.getCode().equals(cityCode) || !subCity.getCode().equals(subCityCode)){
							this.proximitySubCityList.add(new SelectItem(subCity.getCode(), subCity.getName()));
						}
					}
					
					this.proximitySubCityList = new FilterList<SelectItem>(GlazedLists.eventList(proximitySubCityList), subCityNameMatcherEditor);
				}
				else{
					clearSubCitySelect();
				}
			}
			else{
				clearSubCitySelect();
			}
			
		}
	}
	
	public void subCitySelect(ValueChangeEvent event){
		
		if (event.getComponent() instanceof SelectInputText) {

			SelectInputText component = (SelectInputText)event.getComponent();
			
			String value = (String) event.getNewValue();
			subCityNameMatcherEditor.setMode(Constants.TEXT_MATCHER_EDITOR_MODE);
			subCityNameMatcherEditor.setFilterText((value == null) ? new String[] {} : new String[] { value });
			
			SelectItem selectedItem = component.getSelectedItem();

			if(selectedItem != null){
				this.selectedSubCity = (String) selectedItem.getValue();
			}
			else{
				this.selectedSubCity = null;
			}

		}
	}
	
	private void clearSubCitySelect(){
		this.proximitySubCityList = null;
		this.compSelectSubCity.setValue(null);
	}
	
	private boolean valid(){
		boolean valid = true;
		
		if(compSelectCity.getValue() == null || compSelectCity.getValue().equals("")){
			JSFUtils.addFacesErrorMessage(compSelectCity, UIInput.REQUIRED_MESSAGE_ID);
			valid = false;
		}
		if(compSelectSubCity.getValue() == null || compSelectSubCity.getValue().equals("")){
			JSFUtils.addFacesErrorMessage(compSelectSubCity, UIInput.REQUIRED_MESSAGE_ID);
			valid = false;
		}
		return valid;
	}
	
	private City findCityByCode(String cityCode){
		return this.cities.get(cities.indexOf(new City(cityCode,"")));
	}

	private void clearSubCityList(){
		this.subCityList.clear();
		this.subCityList.add(new SelectItem(Constants.SELECT_VALUE, Constants.SELECT_LABEL));
	}
	
	public void valueChangeSubCity(ValueChangeEvent event){
		String subCityCode = (String)event.getNewValue();
		
		if(subCityCode == null){
			tableHeader = STATIC_TABLE_HEADER;
			selectCityMsg = STATIC_SELECT_CITY_MSG;
			selectSubCityMsg = STATIC_SELECT_SUBCITY_MSG;
			this.subsInProximity = null;
			return;
		}
		
		
		if(subCities != null && !subCities.isEmpty()){
			for(SubCity subCity : subCities){
				if(subCity.getCode().equals(subCityCode)){
					
					tableHeader = STATIC_TABLE_HEADER + subCity.getName();
					selectCityMsg = STATIC_SELECT_CITY_MSG + "to " + subCity.getName();
					selectSubCityMsg = STATIC_SELECT_SUBCITY_MSG + "to " + subCity.getName();
					this.subsInProximity = new ArrayList<SubCity>(subCity.getSubCitiesInProximity());
					break;
				}
			}
		}
	}
	
	public void submit(ActionEvent event){
		
		if(!valid()){
			return;
		}
		
		SubCity selectedSubCity = findSubCityByCode(this.subCities, this.subCityCode);

		if(this.selectedSubCity == null){
			JSFUtils.addFacesErrorMessage(compSelectSubCity, "invalid.subcity");
			return;
		}
		SubCity closeSubCity = findSubCityByCode(this.proximitySubCities, this.selectedSubCity);
		
		//If the table is not empty.
		if(this.subsInProximity != null && !this.subsInProximity.isEmpty()){
			/* If a selected Sub City has already been marked as a Sub City in close
			 * Proximity, remove it from the list*/
			if(this.subsInProximity.contains(closeSubCity)){
				return;
			}
		}
		try{
			Set<SubCity> savedCollection = subCityService.setProximity(selectedSubCity, closeSubCity);

			//For Version Controlling. Replace the Sub Cities with the modified ones.
			for(SubCity savedSubCity : savedCollection){
				if(this.subCities.contains(savedSubCity)){
					this.subCities.set(subCities.indexOf(savedSubCity), savedSubCity);
				}

				if(this.proximitySubCities.contains(savedSubCity)){
					this.proximitySubCities.set(proximitySubCities.indexOf(savedSubCity), savedSubCity);
				}

				City updatedCity = findCityByCode(savedSubCity.getCity().getCode());
				updatedCity.getSubcities().remove(savedSubCity);
				updatedCity.getSubcities().add(savedSubCity);
			}

			this.subsInProximity = new ArrayList<SubCity>
			(this.subCities.get(subCities.indexOf(selectedSubCity)).getSubCitiesInProximity());

		}catch(Throwable e){
			return;
		}
	}
	
	private SubCity findSubCityByCode(List<SubCity> subCities, String code){
		if(subCities != null && !subCities.isEmpty()){
			for(SubCity subCity : subCities){
				if(subCity.getCode().equals(code)){
					return subCity;
				}
			}
		}
		return null;
	}
	
	public void removeProximity(ActionEvent event){
		
		String selectedSubCode = (String)JSFUtils.getRequestParameter("subCityCode");
		SubCity selectedSubCity = findSubCityByCode(this.subCities, this.subCityCode);
		SubCity removingSubCity = findSubCityByCode(this.subsInProximity, selectedSubCode);
		
		Map<String, SubCity> savedMap = null;
		try{
			savedMap = subCityService.removeProximity(selectedSubCity, removingSubCity);
		}catch(Throwable e){
			return;
		}
		
		this.subsInProximity.remove(removingSubCity);
		
		//For version controlling
		SubCity savedSelectedSub = savedMap.get(Constants.SELECTED_SUBCITY);
		SubCity savedRemovedSub = savedMap.get(Constants.REMOVED_SUBCITY);
		
		subCities.set(subCities.indexOf(savedSelectedSub), savedSelectedSub);
		if(subCities.contains(savedRemovedSub)){
			subCities.set(subCities.indexOf(savedRemovedSub), savedRemovedSub);
		}
		
		if(this.proximitySubCities != null){
			if(proximitySubCities.contains(savedRemovedSub)){
				proximitySubCities.set(proximitySubCities.indexOf(savedRemovedSub), savedRemovedSub);
			}
			if(proximitySubCities.contains(savedSelectedSub)){
				proximitySubCities.set(proximitySubCities.indexOf(savedSelectedSub), savedSelectedSub);
			}
		}
		City cityOfRemovedSub = findCityByCode(savedRemovedSub.getCity().getCode());
		cityOfRemovedSub.getSubcities().remove(savedRemovedSub);
		cityOfRemovedSub.getSubcities().add(savedRemovedSub);
		
		City cityOfSelectedSub = findCityByCode(savedSelectedSub.getCity().getCode());
		cityOfSelectedSub.getSubcities().remove(savedSelectedSub);
		cityOfSelectedSub.getSubcities().add(savedSelectedSub);
	}
	
	private void clear(){
		clearSubCityList();
		this.subsInProximity = null;
		this.cityCode = null;
		this.subCityCode = null;
		this.selectedCity = null;
		selectCityMsg = STATIC_SELECT_CITY_MSG;
		selectSubCityMsg = STATIC_SELECT_SUBCITY_MSG;
		tableHeader = STATIC_TABLE_HEADER;
		
		compSubCitylist.resetValue();
	}

	public String getCityCode() {
		return cityCode;
	}

	public void setCityCode(String cityCode) {
		this.cityCode = cityCode;
	}

	public String getSubCityCode() {
		return subCityCode;
	}

	public void setSubCityCode(String subCityCode) {
		this.subCityCode = subCityCode;
	}

	public boolean isCitySelected() {
		return citySelected;
	}

	public void setCitySelected(boolean citySelected) {
		this.citySelected = citySelected;
	}

	public List<SelectItem> getCityList() {
		return cityList;
	}

	public void setCityList(List<SelectItem> cityList) {
		this.cityList = cityList;
	}

	public List<SelectItem> getSubCityList() {
		return subCityList;
	}

	public void setSubCityList(List<SelectItem> subCityList) {
		this.subCityList = subCityList;
	}

	public HtmlSelectOneMenu getCompCitylist() {
		return compCitylist;
	}

	public void setCompCitylist(HtmlSelectOneMenu compCitylist) {
		this.compCitylist = compCitylist;
	}

	public HtmlSelectOneMenu getCompSubCitylist() {
		return compSubCitylist;
	}

	public void setCompSubCitylist(HtmlSelectOneMenu compSubCitylist) {
		this.compSubCitylist = compSubCitylist;
	}

	/*public HtmlSelectOneListbox getCompCity() {
		return compCity;
	}

	public void setCompCity(HtmlSelectOneListbox compCity) {
		this.compCity = compCity;
	}

	public HtmlSelectManyListbox getCompSubCities() {
		return compSubCities;
	}

	public void setCompSubCities(HtmlSelectManyListbox compSubCities) {
		this.compSubCities = compSubCities;
	}*/

	public void setServiceLocator(ServiceLocator serviceLocator) {
		this.serviceLocator = serviceLocator;
	}

	public List<SubCity> getSubsInProximity() {
		return subsInProximity;
	}

	public void setSubsInProximity(List<SubCity> subsInProximity) {
		this.subsInProximity = subsInProximity;
	}

	public List<SelectItem> getProximitySubCityList() {
		return proximitySubCityList;
	}

	public void setProximitySubCityList(List<SelectItem> proximitySubCityList) {
		this.proximitySubCityList = proximitySubCityList;
	}

	public String getSelectedCity() {
		return selectedCity;
	}

	public void setSelectedCity(String selectedCity) {
		this.selectedCity = selectedCity;
	}

	public List<SelectItem> getProximityCityList() {
		return proximityCityList;
	}

	public void setProximityCityList(List<SelectItem> proximityCityList) {
		this.proximityCityList = proximityCityList;
	}

	/*public String[] getSelectedProximitySubCityList() {
		return selectedProximitySubCityList;
	}

	public void setSelectedProximitySubCityList(
			String[] selectedProximitySubCityList) {
		this.selectedProximitySubCityList = selectedProximitySubCityList;
	}*/

	public String getSelectCityMsg() {
		return selectCityMsg;
	}

	public void setSelectCityMsg(String selectCityMsg) {
		this.selectCityMsg = selectCityMsg;
	}

	public String getSelectSubCityMsg() {
		return selectSubCityMsg;
	}

	public void setSelectSubCityMsg(String selectSubCityMsg) {
		this.selectSubCityMsg = selectSubCityMsg;
	}

	public String getTableHeader() {
		return tableHeader;
	}

	public void setTableHeader(String tableHeader) {
		this.tableHeader = tableHeader;
	}

	public SelectInputText getCompSelectCity() {
		return compSelectCity;
	}

	public void setCompSelectCity(SelectInputText compSelectCity) {
		this.compSelectCity = compSelectCity;
	}

	public String getSelectedSubCity() {
		return selectedSubCity;
	}

	public void setSelectedSubCity(String selectedSubCity) {
		this.selectedSubCity = selectedSubCity;
	}

	public SelectInputText getCompSelectSubCity() {
		return compSelectSubCity;
	}

	public void setCompSelectSubCity(SelectInputText compSelectSubCity) {
		this.compSelectSubCity = compSelectSubCity;
	}
	
	
	
	
	
	

}
